/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Global Trust Authority is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::future::Future;
use std::pin::Pin;
use serde_json::{json, Value};
use std::fs;
use std::collections::HashMap;

use attestation::{
    chain::{
        attestation_chain::{AttestationContext, AttestationHandler},
        handlers::{
            nonce_validation_handler::NonceValidationHandler,
            nonce_processing_handler::NonceProcessingHandler,
            export_policy_handler::ExportPolicyHandler,
            evidence_response_handler::EvidenceResponseHandler,
        },
    },
    entities::
        attest_request::{Evidence, Measurement}
    ,
    error::attestation_error::AttestationError,
    factory::token_factory::TokenFactory,
};
use plugin_manager::{PluginManager, PluginManagerInstance, ServiceHostFunctions, ServicePlugin, PluginError};
use async_trait::async_trait;
use config_manager::types::context::{CONFIG, AttestationService, Policy, ExportPolicyFile, ServerConfig, KeyManagement, TokenManagement, Cert, NonceConfig, RefValue};
use std::sync::Once;

#[cfg(test)]
mod tests {
    use super::*;

    fn setup() {
        let _ = common_log::init();
        setup_plugin_manager();
        setup_config();
    }

    fn create_test_policy() -> Policy {
        let export_policy_file = ExportPolicyFile {
            name: "test_attester".to_string(),
            path: format!("{}/tests/data/test_export_policy.rego", env!("CARGO_MANIFEST_DIR")),
        };
        Policy {
            export_policy_file: vec![export_policy_file],
            is_verify_policy_signature: false,
            single_user_policy_limit: 100,
            policy_content_size_limit: 1024000,
            query_user_policy_limit: 100,
        }
    }

    fn create_test_key_management() -> KeyManagement {
        KeyManagement {
            vault_get_key_url: "http://localhost:8200".to_string(),
            is_require_sign: false,
            key_ca_cert_path: "/tmp/ca.crt".to_string(),
            key_cli_key_path: "/tmp/client.key".to_string(),
            key_cli_cert_path: "/tmp/client.crt".to_string(),
        }
    }

    fn create_test_token_management() -> TokenManagement {
        TokenManagement {
            jku: "http://localhost:8080/jwks".to_string(),
            kid: "test-key-id".to_string(),
            exist_time: 3600000,
            iss: "test-issuer".to_string(),
            eat_profile: "test-profile".to_string(),
            mq_enabled: false,
            token_topic: "test-topic".to_string(),
            token_signing_algorithm: "RS256".to_string(),
        }
    }

    fn create_test_attestation_service() -> AttestationService {
        AttestationService {
            key_management: create_test_key_management(),
            token_management: create_test_token_management(),
            policy: create_test_policy(),
            ref_value: RefValue {
                single_user_ref_value_limit: 10,
            },
            cert: Cert {
                single_user_cert_limit: 100,
            },
            nonce: NonceConfig {
                nonce_valid_period: 300,
                nonce_bytes: 32,
            },
            plugins: vec![],
        }
    }

    fn setup_config() {
        static INIT: Once = Once::new();
        let _ = INIT.call_once(|| {
            let config = ServerConfig {
                attestation_common: None,
                attestation_service: create_test_attestation_service(),
            };
            let _ = CONFIG.set_instance(config);
        });
    }

    // Mock plugin for testing evidence verification
    #[allow(dead_code)]
    struct MockTestPlugin;

    impl plugin_manager::PluginBase for MockTestPlugin {
        fn plugin_type(&self) -> &str {
            "test_attester"
        }
    }

    #[async_trait]
    impl ServicePlugin for MockTestPlugin {
        fn get_sample_output(&self) -> Value {
            json!({
                "verification_result": true,
                "evidence_data": "test_evidence"
            })
        }

        async fn verify_evidence(
            &self,
            _user_id: &str,
            _node_id: Option<&str>,
            _evidence: &Value,
            _nonce: Option<&[u8]>,
        ) -> Result<Value, PluginError> {
            Ok(json!({
                "verification_result": true,
                "evidence_data": "test_evidence",
                "status": "verified"
            }))
        }
    }

    // Mock host functions for testing
    fn create_test_host_functions() -> ServiceHostFunctions {
        ServiceHostFunctions {
            validate_cert_chain: Box::new(|_, _, cert_data| Box::pin(async move {
                !cert_data.is_empty()
            })),
            get_unmatched_measurements: Box::new(|measured_values, _, _| Box::pin(async move {
                Ok(measured_values.clone())
            })),
            query_configuration: |s| if s == "test" { Some("test".to_string()) } else { Some("default".to_string()) },
        }
    }

    fn build_mock_plugin() -> Result<std::path::PathBuf, String> {
        use std::process::Command;
        use std::path::Path;
        
        // Get the path to the mock plugin source
        let manifest_dir = match std::env::var("CARGO_MANIFEST_DIR") {
            Ok(dir) => dir,
            Err(_) => return Err("CARGO_MANIFEST_DIR not set".to_string()),
        };
        
        // Use platform-agnostic path joining
        let mock_plugin_dir = Path::new(&manifest_dir).join("tests").join("mock_test_attester");
        
        if !mock_plugin_dir.exists() {
            return Err(format!("Mock plugin directory not found at {:?}", mock_plugin_dir));
        }
        
        // Check if Cargo.toml exists
        let cargo_toml = mock_plugin_dir.join("Cargo.toml");
        if !cargo_toml.exists() {
            return Err(format!("Cargo.toml not found at {:?}", cargo_toml));
        }
        
        // Build the mock plugin
        // Use platform-agnostic cargo command
        let cargo = if cfg!(windows) { "cargo.exe" } else { "cargo" };
        
        let output = match Command::new(cargo)
            .args(["build", "--release"])
            .current_dir(&mock_plugin_dir)
            .output() {
                Ok(output) => output,
                Err(e) => return Err(format!("Failed to execute cargo build: {}", e)),
            };
        
        if !output.status.success() {
            return Err(format!("Failed to build mock plugin: {}", 
                String::from_utf8_lossy(&output.stderr)));
        }
        
        // Return the path to the built plugin library using platform-specific naming
        let target_dir = mock_plugin_dir.join("target").join("release");
        
        let lib_filename = get_platform_lib_filename("mock_test_attester");
        
        let lib_path = target_dir.join(lib_filename);
        
        if !lib_path.exists() {
            return Err(format!("Plugin library not found at {:?}", lib_path));
        }
        
        Ok(lib_path)
    }

    fn get_platform_lib_filename(name: &str) -> String {
        #[cfg(target_os = "windows")]
        {
            format!("{}.dll", name)
        }
        
        #[cfg(target_os = "linux")]
        {
            format!("lib{}.so", name)
        }
        
        #[cfg(not(any(target_os = "windows", target_os = "linux")))]
        {
            compile_error!("Unsupported platform for tests")
        }
    }

    // Initialize plugin manager with mock plugin for testing
    fn setup_plugin_manager() {
        let host_functions = create_test_host_functions();
        let manager = PluginManager::<dyn ServicePlugin, ServiceHostFunctions>::get_instance();
        
        if !manager.is_initialized() {
            // Build the mock plugin first
            let mock_plugin_path = match build_mock_plugin() {
                Ok(path) => path,
                Err(e) => {
                    eprintln!("Failed to build mock plugin: {}", e);
                    return;
                }
            };
            
            // Create plugin paths map with our compiled mock plugin
            let mut plugin_paths = HashMap::new();
            plugin_paths.insert("test_attester".to_string(), mock_plugin_path.to_string_lossy().to_string());
            
            manager.initialize(&plugin_paths, &host_functions);
        }
    }

    fn create_test_measurement() -> Measurement {
        let json_content = fs::read_to_string("tests/data/test_measurement.json")
            .expect("Failed to read test_measurement.json");
        let json_data: Value = serde_json::from_str(&json_content)
            .expect("Failed to parse test_measurement.json");
        
        let measurement_data = &json_data["measurements"][0];
        
        // Extract evidences
        let evidences_data = &measurement_data["evidences"];
        let mut evidences = Vec::new();
        
        for evidence_data in evidences_data.as_array().unwrap() {
            evidences.push(Evidence {
                attester_type: evidence_data["attester_type"].as_str().unwrap().to_string(),
                evidence: evidence_data["evidence"].clone(),
                policy_ids: None, // Not present in the JSON data
            });
        }
        
        Measurement {
            node_id: measurement_data["node_id"].as_str().unwrap().to_string(),
            nonce_type: Some(measurement_data["nonce_type"].as_str().unwrap().to_string()),
            nonce: None, // Not present in the JSON data
            token_fmt: None, // Not present in the JSON data
            attester_data: Some(Value::String(measurement_data["attester_data"].as_str().unwrap().to_string())),
            evidences,
        }
    }

    fn create_test_evidence() -> Evidence {
        Evidence {
            attester_type: "test_attester".to_string(),
            evidence: json!({"test": "evidence"}),
            policy_ids: Some(vec!["policy1".to_string()]),
        }
    }

    fn create_test_context() -> AttestationContext {
        let token_factory = TokenFactory::new();
        let token_instance = token_factory.create_token("eat").expect("Failed to create token instance");
        
        AttestationContext::new(
            create_test_measurement(),
            create_test_evidence(),
            "test_user".to_string(),
            token_instance,
        )
    }

    /// Helper function to create mock verification evidence result
    /// This simulates what EvidenceVerification processor would output
    fn create_mock_verify_evidence() -> serde_json::Value {
        json!({
            "verification_result": true,
            "evidence_data": "test_evidence",
            "status": "verified"
        })
    }

    struct MockProcessor {
        name: String,
        should_fail: bool,
        next: Option<Box<dyn AttestationHandler>>,
    }

    impl MockProcessor {
        fn new(name: &str, should_fail: bool) -> Self {
            Self {
                name: name.to_string(),
                should_fail,
                next: None,
            }
        }
    }

    impl AttestationHandler for MockProcessor {
        fn handle<'a>(&'a self, _context: &'a mut AttestationContext) 
            -> Pin<Box<dyn Future<Output = Result<(), AttestationError>> + Send + 'a>> {
            Box::pin(async move {
                if self.should_fail {
                    Err(AttestationError::InvalidParameter(format!("{} failed", self.name)))
                } else {
                    Ok(())
                }
            })
        }

        fn set_next(&mut self, next: Box<dyn AttestationHandler>) {
            self.next = Some(next);
        }

        fn name(&self) -> &'static str {
            "MockProcessor"
        }

        fn get_next(&self) -> Option<&dyn AttestationHandler> {
            self.next.as_ref().map(|n| n.as_ref())
        }
    }

    #[tokio::test]
    async fn test_create_empty_attestation_chain_should_execute_successfully() {
        setup();
        
        // Test with empty handler chain
        let handler = MockProcessor::new("empty", false);
        let mut context = create_test_context();
        
        let result = handler.execute(&mut context).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_add_multiple_handlers_to_chain_should_execute_in_order() {
        setup();
        
        // Create a chain of handlers
        let mut first = MockProcessor::new("test1", false);
        let second = MockProcessor::new("test2", false);
        first.set_next(Box::new(second));
        
        let mut context = create_test_context();
        let result = first.execute(&mut context).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_chain_execution_should_fail_when_processor_returns_error() {
        setup();
        
        // Create a chain where the second handler fails
        let mut first = MockProcessor::new("first", false);
        let mut second = MockProcessor::new("second", true);
        let third = MockProcessor::new("third", false);
        second.set_next(Box::new(third));
        first.set_next(Box::new(second));
        
        let mut context = create_test_context();
        let result = first.execute(&mut context).await;
        
        assert!(result.is_err());
        match result.unwrap_err() {
            AttestationError::InvalidParameter(msg) => {
                assert!(msg.contains("second failed"));
            }
            _ => panic!("Expected InvalidParameter error"),
        }
    }

    #[tokio::test]
    async fn test_create_attestation_context_should_initialize_with_correct_values() {
        setup();
        
        let token_factory = TokenFactory::new();
        let token_instance = token_factory.create_token("eat").expect("Failed to create token instance");
        
        let context = AttestationContext::new(
            create_test_measurement(),
            create_test_evidence(),
            "test_user".to_string(),
            token_instance,
        );
        
        assert_eq!(context.user_id, "test_user");
        assert_eq!(context.measurement.node_id, "a4e7c719-6b05-4ac6-b95a-7e71a9d6f9d5");
        assert_eq!(context.evidence.attester_type, "test_attester");
        // We can't directly test token_instance existence with is_some() since it's not Option<T>
        // The token_instance is always present after construction
        assert!(context.nonce_bytes.is_none());
        assert!(context.aggregate_nonce_bytes.is_none());
        assert!(context.verify_evidence.is_none());
        assert!(context.evidence_token_response.is_none());
    }

    #[tokio::test]
    async fn test_nonce_validation_processor_should_handle_context_successfully() {
        setup();
        
        let processor = NonceValidationHandler::new();
        let mut context = create_test_context();
        
        let result = processor.handle(&mut context).await;
        if let Err(ref error) = result {
            println!("Test failed with error: {:?}", error);
        }
        assert!(result.is_ok());
        
        assert_eq!(processor.name(), "NonceValidation");
    }

    #[tokio::test]
    async fn test_nonce_processing_processor_should_succeed_with_ignore_nonce_type() {
        setup();
        
        let processor = NonceProcessingHandler::new();
        let mut context = create_test_context();
        
        let result = processor.handle(&mut context).await;
        assert!(result.is_ok(), "NonceProcessing should succeed with nonce_type 'ignore'");
        
        assert!(context.nonce_bytes.is_none(), "nonce_bytes should be None for nonce_type 'ignore'");
        
        assert!(context.aggregate_nonce_bytes.is_some(), "aggregate_nonce_bytes should be Some when attester_data exists");
        
        assert_eq!(processor.name(), "NonceProcessing");
    }

    #[tokio::test]
    async fn test_export_policy_processor_should_handle_mock_evidence_successfully() {
        setup();
        
        let processor = ExportPolicyHandler::new();
        let mut context = create_test_context();
        // Use mock verification evidence that simulates EvidenceVerification output
        context.verify_evidence = Some(create_mock_verify_evidence());
        
        let result = processor.handle(&mut context).await;
        
        assert!(result.is_ok());
        
        assert_eq!(processor.name(), "ExportPolicy");
    }

    #[tokio::test]
    async fn test_evidence_response_processor_should_set_token_response() {
        setup();
        
        // Create context with token instance directly
        let mut context = create_test_context();
        
        // Now test EvidenceResponse processor
        let processor = EvidenceResponseHandler::new();
        let result = processor.handle(&mut context).await;
        
        // Should succeed because token_instance is now present
        assert!(result.is_ok());
        assert!(context.evidence_token_response.is_some(), "evidence_token_response should be set");
        
        assert_eq!(processor.name(), "EvidenceResponse");
    }
}